/*********************************
 * jsmud: JavaScript MUD Kernel  *
 *          Mojodo, Inc.         *
 *        Sean Middleditch       *
 * Copyright (C) 2005,2007,2008  *
 *      All rights reserved.     *
 *   See COPYING for licensing.  *
 *********************************/

/* ==== SYSTEM INCLUDES ==== */

#include <string.h>
#include <errno.h>
#include <limits.h>
#include <arpa/telnet.h>

/* ==== LIBRARY INCLUDES ==== */

#include <sqlite3.h>
#include <zlib.h>

#define XP_UNIX 1
#define MOZILLA_1_8_BRANCH 1
#include <jsapi.h>
#undef XP_UNIX
#undef MOZILLA_1_8_BRANCH

/* ==== JSMUD HEADER ==== */

#include "jsmud.h"

/* ==== GLOBALS ==== */

JSRuntime* js_runtime = NULL;
JSContext* js_context = NULL;
JSObject* js_global = NULL;
JSClass js_database_class;
JSClass js_connection_class;

/* ==== FUNCTIONS ==== */

int js_init (void)
{
	jsval rval;

	/* create runtime */
	js_runtime = JS_NewRuntime(32L * 1024L * 1024L); /* 32MB */
	if (js_runtime == NULL) {
		log_error("JS_NewRuntime() failed");
		return 0;
	}

	/* create context */
	js_context = JS_NewContext(js_runtime, 8096);
	if (js_context == NULL) {
		log_error("JS_NewContext() failed");
		return 0;
	}

	/* set options */
	JS_SetOptions(js_context, JSOPTION_VAROBJFIX|JSOPTION_COMPILE_N_GO|JSOPTION_XML);

	/* set error handler */
	JS_SetErrorReporter(js_context, &javascript_error);

	/* make global object */
	js_global = JS_NewObject(js_context, NULL, NULL, NULL);
	if (js_global == NULL) {
		log_error("JS_NewObject() failed");
		return 0;
	}
	JS_SetGlobalObject(js_context, js_global);

	/* add standard classes and stuff to global object */
	if (!JS_InitStandardClasses(js_context, js_global)) {
		log_error("JS_InitStandardClasses() failed");
		return 0;
	}

	/* initialize bindings */
	if (!init_stdlib())
		return 0;
	if (!init_filelib())
		return 0;
	if (!init_netlib())
		return 0;
	if (!init_timerlib())
		return 0;
	if (!init_dblib())
		return 0;

	/* run the stdlib code */
	if (!JS_EvaluateScript(js_context, js_global, stdlib_js, stdlib_js_len, "jsmud:stdlib.js", 1, &rval)) {
		log_error("JS_EvaluateScript() for built-in stdlib.js failed");
		return 0;
	}

	return 1;
}

int js_main (void)
{
	JSScript* script;
	jsval rval;
	char path[PATH_MAX];

	/* run the main script */
	if (snprintf(path, sizeof(path), "%s/main.js", opt_data_path) > sizeof(path)) {
		log_error("cannot open %s/main.js: path too long", opt_data_path);
		return 0;
	}
	if ((script = JS_CompileFile(js_context, js_global, path)) == NULL) {
		log_error("JS_CompileFile() failed");
		return 0;
	}
	if (!JS_ExecuteScript(js_context, js_global, script, &rval)) {
		log_error("Execution of main.js failed");
		return 0;
	}
	JS_DestroyScript(js_context, script);

	return 1;
}

void js_raise (JSContext* cx, const char* fmt, ...)
{
	va_list va;
	char buffer[4096];
	jsval str;

	va_start(va, fmt);
	vsnprintf(buffer, sizeof(buffer), fmt, va);
	va_end(va);

	str = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, buffer));
	JS_SetPendingException(cx, str);
}

void javascript_error (JSContext* context, const char* msg, JSErrorReport* error)
{
	/* log to the log file if we have one */
	if (log_file != NULL) {
		log_time(log_file);
		fprintf(log_file, "Error: %s", msg);
		if (error->filename != NULL)
			fprintf(log_file, " at file %s:%u", error->filename, error->lineno);
		else if (error->lineno != 0)
			fprintf(log_file, " at line %u", error->lineno);
		if (error->linebuf != NULL)
			fprintf(log_file, ": %s", error->linebuf);
		fprintf(log_file, "\n");
	}

	/* log to stderr if we have no log file or if quiet mode is off */
	if (log_file == NULL || !log_quiet) {
		fprintf(stderr, "Error: %s", msg);
		if (error->filename != NULL)
			fprintf(stderr, " at file %s:%u", error->filename, error->lineno);
		else if (error->lineno != 0)
			fprintf(stderr, " at line %u", error->lineno);
		if (error->linebuf != NULL)
			fprintf(stderr, ": %s", error->linebuf);
		fprintf(stderr, "\n");
	}
}

JSBool js_call_method (JSObject* object, const char* name, int argc, jsval* argv)
{
	JSFunction* function;
	jsval val;

	if (JS_GetProperty(js_context, object, name, &val) == JS_FALSE)
		return JS_FALSE;

	if (JSVAL_IS_VOID(val))
		return JS_FALSE;

	if ((function = JS_ValueToFunction(js_context, val)) == NULL)
		return JS_FALSE;

	return JS_CallFunction(js_context, object, function, argc, argv, &val);
}

JSObject* jsval_to_object (jsval val)
{
	JSObject* object;

	if (JS_ValueToObject(js_context, val, &object) == JS_FALSE)
		return NULL;

	return object;
}

/* vim: set tabstop=2 shiftwidth=2 ai: */
